﻿using HLStock.Common;
using HLStock.DBAccess.AOP;
using HLStock.Model;
using HLStock.MyExcep;
using System.Data;
using System.Text;
using static System.Runtime.InteropServices.JavaScript.JSType;

namespace HLStock.DBAccess
{
    public class DbUpdater<T> where T : BaseModel, new()
    {
        protected DbInstance instance;
        protected string tableName;
        protected OnlineUser onlineUser;
        protected List<PropValueGetter> PropGetters;
        protected List<IBeforeSaveAction<T>> BeforeSaveHandlers = new List<IBeforeSaveAction<T>>();
        protected List<IBeforeDeleteAction<T>> BeforeDeleteHandlers = new List<IBeforeDeleteAction<T>>();
        protected List<IAfterSaveAction<T>> AfterSaveHandlers = new List<IAfterSaveAction<T>>();
        protected List<IAfterDeleteAction<T>> AfterDeleteHandlers = new List<IAfterDeleteAction<T>>();

        public DbUpdater(DbInstance instance)
        {
            this.instance = instance;
            tableName = DataAccessUtils.GetTableName<T>();
            PropGetters = DataAccessUtils.GetGetters<T>();
            onlineUser = ServiceLoader.GetService<OnlineUser>();
            RegDefaultAop();
        }
        public virtual void RegDefaultAop()
        {
            this.BeforeSaveHandlers.Add(new FlagCompareAction<T>());
            this.BeforeSaveHandlers.Add(new ForeignCheckAction<T>());
            this.BeforeSaveHandlers.Add(new ReadonlyCheckAction<T>());
            this.BeforeSaveHandlers.Add(new UniqueCheckAction<T>());
        }
        public virtual List<IBeforeSaveAction<T>> RegBeforeSaveHandlers(IBeforeSaveAction<T> action)
        {
            this.BeforeSaveHandlers.Add(action);
            return this.BeforeSaveHandlers;
        }
        public virtual List<IAfterSaveAction<T>> RegAfterSaveHandlers(IAfterSaveAction<T> action)
        {
            this.AfterSaveHandlers.Add(action);
            return this.AfterSaveHandlers;
        }
        public virtual List<IAfterDeleteAction<T>> RegAfterDeleteHandlers(IAfterDeleteAction<T> action)
        {
            this.AfterDeleteHandlers.Add(action);
            return this.AfterDeleteHandlers;
        }
        public virtual List<IBeforeDeleteAction<T>> RegBeforeDeleteHandlers(IBeforeDeleteAction<T> action)
        {
            this.BeforeDeleteHandlers.Add(action);
            return BeforeDeleteHandlers;
        }
        protected virtual T QueryFromDb(string pk)
        {
            var qryInstance = new DbQuery<T>(instance);
            var dataInDb = qryInstance.Where(CondCollection.True().Eq(nameof(BaseModel.Pk), pk)).First();
            return dataInDb;
        }
        public virtual void Save(T data)
        {
            if (data == null) throw new MyEmptyOrNullDataException("传入的参数为空");
            T dataInDb = null;
            if (!string.IsNullOrWhiteSpace(data.Pk))
                dataInDb = QueryFromDb(data.Pk);
            FillAuditField(data);
            var e = new BeforeSaveEventArgs<T>(data, dataInDb);
            foreach (var handler in BeforeSaveHandlers)
            {
                handler.Handle(e);
                if (e.Cancle)
                {
                    throw new MyDataOptException(e.Message);
                }
            }
            if (data.Flag == 1)
            {
                Insert(data);
            }
            else
            {
                Update(data,dataInDb);
            }
            var e2 = new AfterSaveOrDeleteEventArgs<T>(data);
            foreach (var handler in AfterSaveHandlers)
            {
                handler.Handle(e2);
            }
        }
        public virtual void Delete(string pk)
        {
            if (pk == null) throw new MyEmptyOrNullDataException("传入的参数为空");
            var dataInDb = QueryFromDb(pk);
            var e = new BeforeDeleteEventArgs<T>(dataInDb);
            foreach (var handler in BeforeDeleteHandlers)
            {
                handler.Handle(e);
                if (e.Cancle)
                {
                    throw new MyDataOptException(e.Message);
                }
            }
            FillAuditFieldForDelete(dataInDb);
            var cmd = DataAccessUtils.GetDeleteSql<T>(dataInDb, instance);
            using (SqlCostRec.Start(cmd))
            {
                int result = 0;
                try
                {
                    result = cmd.ExecuteNonQuery();
                }
                catch (Exception ex)
                {
                    MyLogger.Err(ExceptionUtil.FormatExceptionInfo(ex));
                }
                if (result == 0)
                {
                    throw new MySaveOrDelNotSuccessException<T>("本批删除全部失败");
                }
            }
            var e2 = new AfterSaveOrDeleteEventArgs<T>(dataInDb);
            foreach (var handler in AfterDeleteHandlers)
            {
                handler.Handle(e2);
            }
        }
        public virtual void ForceUpdateCol(string whereCol, string setCol, object whereVal, object setVal)
        {
            if (!DataAccessUtils.IsColumnName4Sql(whereCol) || !DataAccessUtils.IsColumnName4Sql(setCol))
                throw new MySqlException("SQL有风险");
            var sql = new StringBuilder("UPDATE ");
            sql.AppendFormat("\"{0}\" SET ", tableName);
            Dictionary<string, object> p = new Dictionary<string, object>();
            sql.Append($"{setCol}=@{setCol}");
            sql.Append(",MODIFYDATE=@MODIFYDATE,FLAG=FLAG+1,MODIFIER=@MODIFIER WHERE DEL = 'N' ");
            sql.Append($"AND {whereCol}=:{whereCol}");
            p.Add($"@{setCol}", setVal);
            p.Add("@MODIFYDATE", DateTime.Now.ToString(Const.DATE_TIME_FORMAT));
            p.Add("@MODIFIER", Const.SUPER_USER);
            p.Add($"@{whereCol}", whereVal);
            try
            {
                var result = instance.MakeQueryCommand(sql.ToString(), p.ToArray()).ExecuteNonQuery();
                if (result == 0)
                {
                    throw new MySaveOrDelNotSuccessException<T>($"找不到{whereCol}={whereVal}记录");
                }
            }
            catch (Exception ex)
            {
                MyLogger.Err(ExceptionUtil.FormatExceptionInfo(ex));
                throw new MySaveOrDelNotSuccessException<T>($"{whereCol}={whereVal}强制写入失败");
            }
        }
        protected virtual void Insert(T data)
        {
            IDbCommand cmd = DataAccessUtils.GetInsertSql<T>(data, instance);
            using (SqlCostRec.Start(cmd))
            {
                int _result = 0;
                try
                {
                    _result = cmd.ExecuteNonQuery();
                }
                catch (Exception ex)
                {
                    MyLogger.Err(ExceptionUtil.FormatExceptionInfo(ex));
                    throw new MyDataOptException("保存失败");
                }
            }
        }
        protected virtual void Update(T data, T dataInDb)
        {
            var changed = DataAccessUtils.GetChange(data, dataInDb);
            if (changed.Count() == 0)
            {
                Utils.Restore(data,dataInDb);
                return; 
            }
            IDbCommand cmd = DataAccessUtils.GetUpdateSql<T>(data, changed, instance);
            using (SqlCostRec.Start(cmd))
            {
                try
                {
                    int _result = cmd.ExecuteNonQuery();
                    if (_result != 1)
                    {
                        MyLogger.Log($"PK={data.Pk}的原始数据已经被修改或删除");
                        throw new MyDataOptException("保存失败");
                    }
                }
                catch (Exception ex)
                {
                    MyLogger.Err($"SQL写入失败,PK={data.Pk},EX={ExceptionUtil.FormatExceptionInfo(ex)}");
                    throw new MyDataOptException("保存失败");
                }
            }
        }
        protected virtual void FillAuditField(T data)
        {
            if (data.Flag == 0)
            {
                data.Pk = Utils.CreatePk();
                data.CreateDate = DateTime.Now.ToString(Const.DATE_TIME_FORMAT);
                data.Flag = 1;
                data.Creator = onlineUser != null ? onlineUser.UserId : Const.SUPER_USER;
            }
            else
            {
                data.ModifyDate = DateTime.Now.ToString(Const.DATE_TIME_FORMAT);
                data.Flag += 1;
                data.Modifier = onlineUser != null ? onlineUser.UserId : Const.SUPER_USER;
            }
        }
        protected virtual void FillAuditFieldForDelete(T data)
        {
            data.DeleteDate = DateTime.Now.ToString(Const.DATE_TIME_FORMAT);
            data.DeleteUser = onlineUser != null ? onlineUser.UserId : Const.SUPER_USER;
            data.Del = true;
        }
    }
}
